package com.pomelo.tool.service;

import com.pomelo.base.core.bean.BizException;
import com.pomelo.base.core.util.TemplateUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.*;

/**
 * @ClassName: GenGradleBuildService
 * @Description:
 * @Author: sunxin
 * @Date: 2021/10/16 11:21
 **/
@Service public class GenGradleBuildService {

    private DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    /**
     * 不需要构建的应用
     */
    private static final List<String> IGNORE_BUILD_APPS = new ArrayList<>();

    static {
        IGNORE_BUILD_APPS.add("emapAuth");
        IGNORE_BUILD_APPS.add("emapfunauth");
        IGNORE_BUILD_APPS.add("emapvalidate");
        IGNORE_BUILD_APPS.add("emapflow");
        IGNORE_BUILD_APPS.add("frReport2");
        IGNORE_BUILD_APPS.add("jzreport");
        IGNORE_BUILD_APPS.add("emapcomponent");
        IGNORE_BUILD_APPS.add("ROOT");
    }

    /**
     * source 源文件，代表的是一个源文件
     */
    private static final String KIND_SRC = "src";

    /**
     * lib 用于指定project依赖的lib
     */
    private static final String KIND_LIB = "lib";

    /**
     * 类文件夹
     */
    private static final String KIND_VAR = "var";

    private int index = 0;

    protected Log logger = LogFactory.getLog(this.getClass());

    public boolean genBuildFile(String schoolCode, String appName) {

        String workspace = DeployService.DEPLOY_PATH_PREFIX + schoolCode + "/";
        String path = workspace + appName;
        File appInfo = new File(path, "app_info.xml");
        if (!appInfo.exists()) {
            return false;
        }
        File appDir = appInfo.getParentFile();

        InputStream is = null;
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        Map<String, Object> model = new HashMap<>();
        try {
            model.put("appName", appName);
            model.put("hasPub", new File(appDir, "pub_src").exists());
            Set<String> dependencies = new LinkedHashSet<String>();
            List<Map<String, Object>> tasks = new ArrayList<Map<String, Object>>();
            getDependenciesAndTasks(appDir, dependencies, tasks, "", workspace);
            if (dependencies.size() == 0) {
                return false;
            }
            index = 0;
            model.put("dependencies", dependencies);
            model.put("tasks", tasks);
            is = this.getClass().getClassLoader().getResourceAsStream("template_gradle.txt");
            fos = new FileOutputStream(new File(appDir, "build.gradle"));
            osw = new OutputStreamWriter(fos, "UTF-8");
            String content = TemplateUtil.parseInputStream(is, model);
            IOUtils.write(content, osw);
        } catch (Exception e) {
            throw new RuntimeException(appName + "生成构建文件失败", e);
        } finally {
            IOUtils.closeQuietly(osw);
            IOUtils.closeQuietly(fos);
            IOUtils.closeQuietly(is);
        }
        return true;
    }

    /**
     * 解析应用依赖
     *
     * @param appDir       应用当前目录
     * @param dependencies 依赖应用
     * @param tasks        任务信息
     * @param parent
     * @throws Exception
     */
    private void getDependenciesAndTasks(File appDir, Set<String> dependencies, List<Map<String, Object>> tasks,
        String parent, String workspace) throws Exception {
        File classPathFile = new File(appDir, ".classpath");
        String appName = appDir.getName();
        if (!classPathFile.exists()) {
            File classes = new File(appDir, "classes");
            if (classes.exists()) {
                return;
            }
            throw new BizException("应用" + appName + "未获取到配置文件[" + classPathFile.getAbsolutePath() + "]");
        }
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(classPathFile);
        NodeList entrys = doc.getElementsByTagName("classpathentry");
        if (entrys == null || entrys.getLength() == 0) {
            return;
        }
        File emapApp = new File(appDir, "EMAP_APP");
        List<String> dependApp = new ArrayList<>();
        if (emapApp.exists()) {
            Properties properties = new Properties();
            properties.load(new FileInputStream(emapApp));
            String parentApp = properties.getProperty("parent");
            // 判断依赖的应用是否有src文件夹，如果有，则认为需要拉取源码
            if (parentApp != null && !parentApp.isEmpty() && new File(appDir.getParent() + "/" + parentApp, "src")
                .exists() && !dependApp.contains(parentApp)) {
                logger.info(appName + "添加依赖应用" + parentApp);
                dependApp.add(parentApp);
            }
        }
        for (int i = 0; i < entrys.getLength(); i++) {
            Element element = (Element)entrys.item(i);
            String kind = element.getAttribute("kind");
            String cPath = element.getAttribute("path");
            if (KIND_SRC.equals(kind)) {
                continue;
            }
            if (KIND_LIB.equals(kind)) {
                // 默认会将应用下的lib文件夹的jar包自动依赖
                if (cPath.startsWith("lib/")) {
                    continue;
                }
                // 如果路径包含了当前应用下的lib文件夹，认为已经包含在了lib/下
                if (cPath.indexOf(appName + "/lib/") > -1) {
                    continue;
                }

                if (cPath.endsWith(".jar")) {
                    dependencies.add("compile fileTree(dir: '" + cPath + "', include: '*.jar')");
                } else {
                    if ((cPath.endsWith("classes") || cPath.endsWith("pub_classess"))) {
                        String targetAppName = cPath.replace("/classes", "").replace("/pub_classes", "");
                        if (targetAppName.startsWith("/")) {
                            targetAppName = targetAppName.substring(1);
                        }
                        if (!IGNORE_BUILD_APPS.contains(targetAppName) && !appName.equals(targetAppName)) {
                            if (!dependApp.contains(targetAppName)) {
                                logger.info(appName + "添加依赖应用" + targetAppName);
                                dependApp.add(targetAppName);
                            }
                        }

                    }
                    if (cPath.startsWith("/")) {
                        cPath = workspace + cPath.substring(1);
                    }
                    // 依赖类文件夹
                    dependencies.add("compile files('" + cPath + "')");
                }
            }
            if (KIND_VAR.equals(kind)) {
                if (cPath.startsWith("WORKSPACE")) {
                    String targetAppName = cPath.split("/")[1];
                    if (!dependApp.contains(targetAppName) && !IGNORE_BUILD_APPS.contains(targetAppName) && !appName
                        .equals(targetAppName)) {
                        logger.info(appName + "添加依赖应用" + targetAppName);
                        dependApp.add(targetAppName);
                    }
                    // 变量配置，如果路径中有WORKSPACE，则直接依赖指定目录下的公共应用
                    cPath = cPath.replace("WORKSPACE/", workspace);

                    dependencies.add("compile files('" + cPath + "')");
                    continue;
                }
            }
        }
        if (!dependApp.isEmpty() && "".equals(parent)) {
            for (int i = 0; i < dependApp.size(); i++) {
                String dependAppName = dependApp.get(i);
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("index", index);
                index++;
                map.put("appName", dependAppName);
                tasks.add(map);
                File extendApp = new File(workspace, dependAppName + "$A");
                if (extendApp.exists() && !(dependAppName + "$A").equals(appName)) {
                    Map<String, Object> map2 = new HashMap<String, Object>();
                    map2.put("index", index);
                    index++;
                    map2.put("appName", dependAppName + "\\$A");
                    tasks.add(map2);
                }
            }
        }

        if (!dependApp.isEmpty()) {
            for (int i = 0; i < dependApp.size(); i++) {
                String dependAppName = dependApp.get(i);
                dependencies.add("compile fileTree(dir: '" + workspace + dependAppName + "/lib', include: '*.jar')");
                String url = "compile files('" + workspace + dependAppName + "/classes')";
                File extendApp = new File(workspace, dependAppName + "$A");
                if (extendApp.exists() && !(dependAppName + "$A").equals(appName)) {
                    dependencies
                        .add("compile fileTree(dir: '" + workspace + dependAppName + "\\$A/lib', include: '*.jar')");
                    String url2 = "compile files('" + workspace + dependAppName + "\\$A/classes')";
                    if (!dependencies.contains(url2)) {
                        dependencies.add(url2);
                    }
                }
                if (!dependencies.contains(url)) {
                    dependencies.add(url);
                }
                File dependAppDir = new File(appDir.getParentFile(), dependAppName);
                getDependenciesAndTasks(dependAppDir, dependencies, tasks, parent, workspace);
            }
        }

    }

}
